<template>
  <block v-if="showGridOnly">
    <AdjustParameterGrids
      :grids="parameterGrids"
      :transformed-title="control.store.adjustParameterTitle"
      :disabled="disabled"
      :no-prefix="noPrefix"
      :no-suffix="noSuffix"
      @change="setRemoteValue"
      @custom-click="emit('custom-click', $event)"
    >
      <template #prefix>
        <slot name="prefix"></slot>
      </template>
      <template #suffix>
        <slot name="suffix"></slot>
      </template>
    </AdjustParameterGrids>
  </block>
  <HighlightView
    v-else
    class="adjust-parameter-view"
    :title="activedProgramName"
    :title-remark="programRemaining"
    :use-primary-color="headerHighlight"
  >
    <template #title-icon>
      <slot name="title-icon"></slot>
    </template>
    <template #desc>
      <slot name="desc"></slot>
    </template>

    <block>
      <AdjustParameterGrids
        :grids="parameterGrids"
        :transformed-title="control.store.adjustParameterTitle"
        :disabled="disabled"
        :no-prefix="!$slots.prefix || noPrefix"
        :no-suffix="!$slots.suffix || noSuffix"
        @change="setRemoteValue"
        @custom-click="emit('custom-click', $event)"
      >
        <template #prefix>
          <slot name="prefix"></slot>
        </template>
        <template #suffix>
          <slot name="suffix"></slot>
        </template>
      </AdjustParameterGrids>
    </block>
  </HighlightView>
</template>

<script setup lang="ts">
import type { ProgramAvailable, ProgramSelectOption } from '@/api/appliance'
import { COFFEE_BEAN_AMOUNT } from '@/constants/Command'
import type { CustomClickPayload, CustomOption, Option, ValueType } from '../components/parameterCard.vue'
import HighlightView from '../components/highlightView.vue'
import {
  getNumberOptions,
  getEnumOptions,
  generalUnitFormatter,
  setVirtualOptionValue,
} from './availableParameterUtils'
import type { BaseController } from '@/controller/BaseAppliance/definition'
import { TrackType, trackEvent } from '@/dataReport/tracks'
import AdjustParameterGrids, { type ParameterGrid } from './adjustParameterGrids.vue'

const props = defineProps<{
  control: BaseController.Control
  programName?: string
  programRemaining?: string

  /**
   * 样式和视图
   */
  headerHighlight?: boolean // Header 高亮色显示
  showGridOnly?: boolean // 是否仅显示配置 不显示标题
  noPrefix?: boolean
  noSuffix?: boolean

  /**
   * 调节参数部分
   */
  disabled?: boolean

  /**
   * 需要处理为自定义选项的 Key
   */
  customParams?: CustomOption[]

  /**
   * 需要展示成Picker的Key
   *  - 由于 Int、Double、EnumType 类型都可以展示为 Picker
   *    因此需要指定特定 Key 用于判断是否展示为 Picker
   */
  pickerKeys?: string[] | null
}>()
const emit = defineEmits<{
  (e: 'custom-click', payload: CustomClickPayload): void
}>()

const activedProgramName = computed(() => props.programName || props.control.store.currentProgramName || '')
const adjustableOptions = computed(() => props.control.store.adjustableOptions)
const detailOptions = computed(() => props.control.store.detail?.option || [])

/**
 * 生成参数卡片
 */
const parameterGrids = ref<ParameterGrid[]>([])

watch(
  () => adjustableOptions.value,
  () => syncParameterGrids(),
  { immediate: true }
)
watch(
  () => detailOptions.value,
  () => syncParameterGrids(),
  { deep: true }
)

function syncParameterGrids() {
  parameterGrids.value = getParameterGrids()
}

/**
 * 已启用参数
 */
const activedParameterNames = computed<string[]>(() => {
  return parameterGrids.value.reduce((activedNames, grid) => {
    // Checkbox
    if (grid.type === 'Checkbox') {
      grid.options.forEach((checkOption) => {
        let isCheckboxActive = !!checkOption.value
        if (checkOption.customOption) {
          isCheckboxActive = checkOption.customOption.isActive?.(checkOption.value, checkOption) || false
        }

        if (isCheckboxActive) {
          activedNames.push(checkOption.label)
        }
      })
    }
    // 其他类型
    else {
      const activedOption = grid.options.find((option) => option.value === grid.value)
      if (activedOption) {
        // 判断是否启用
        const isActived = typeof activedOption.value !== 'string' || !activedOption.value.includes('.Off')
        if (isActived) {
          // 选项类型
          const category = grid.title
          // 选择值名称
          const optionName = activedOption.label
          // 单位后缀
          const unit = generalUnitFormatter(activedOption.key, activedOption.value, grid.unit)
          const suffix = unit ? ` (${unit})` : ''
          const activedTitle = `${category} ${optionName}${suffix}`
          activedNames.push(activedTitle)
        }
      }
    }
    return activedNames
  }, [] as string[])
})
function getActivedParameterNames() {
  return activedParameterNames.value
}

/**
 * 监听消息
 */
onBeforeMount(() => {
  // 虚拟家电不监听WebSocket
  if (props.control.store.isVirtualAppliance) return

  props.control.remote.addBatchObserve({
    handler: (msg) => setLocalValue(msg.key, msg.value as ValueType),
  })
})
/**
 * 埋点操作
 */

/**
 * 设置数据方法
 */
const gridLocalSetters = computed(() => {
  return parameterGrids.value.reduce((mapping, grid) => {
    if (grid.type === 'Checkbox') {
      grid.options.forEach((option) => {
        if (!option.key) return
        mapping.set(option.key, (value) => {
          option.value = value
        })
      })
    } else {
      mapping.set(grid.key, (value) => {
        grid.value = value
      })
    }
    return mapping
  }, new Map<string, (value: ValueType) => void>())
})
function setLocalValue(key: string, value: ValueType): void {
  const valueSetter = gridLocalSetters.value.get(key)
  if (typeof valueSetter === 'function' && value !== null) {
    valueSetter(value)
  }
}

let previousRemoteKey = ''
let previousRemoteValue!: ValueType
let previousStartTime = new Date().getTime()
const throttleTime = 3000
function setRemoteValue(key: string, value: ValueType) {
  // 虚拟家电直接设置值 不监听ws
  if (props.control.store.isVirtualAppliance) {
    setVirtualOptionValue(props.control, key, value)
    setLocalValue(key, value)
    return
  }

  const previousActionTime = previousStartTime
  const actionTime = new Date().getTime()
  const isInThrottling = actionTime - previousActionTime < throttleTime
  previousStartTime = actionTime

  // 正在节流中 且 选中相同键、值 终止方法
  if (isInThrottling && key === previousRemoteKey && value === previousRemoteValue) {
    return
  }

  previousRemoteKey = key
  previousRemoteValue = value

  trackEvent(
    'before_select_option',
    {
      program_key: props.control.store.currentProgramKey,
      program_name: activedProgramName.value,
      select_option_key: key,
      select_option_value: value,
    },
    TrackType.ApplianceDetail
  )

  return props.control.store.selectProgramOptions(
    [{ key, value }],
    props.control.store.currentProgramKey,
    activedProgramName.value
  )
}

/**
 * 生成数据方法
 */
function getParameterOptions(): ProgramSelectOption[] {
  return parameterGrids.value.reduce((selectOptions, grid) => {
    // Picker Radio 类型
    if (grid.type !== 'Checkbox') {
      if (grid.value === null) return selectOptions
      selectOptions.push({
        key: grid.key,
        value: grid.value,
      })
    }
    // Checkbox 卡片 取options里的值
    else {
      grid.options.forEach((checkOption) => {
        if (checkOption.key) {
          selectOptions.push({
            key: checkOption.key,
            value: checkOption.value,
          })
        }
      })
    }
    return selectOptions
  }, [] as ProgramSelectOption[])
}

function getParameterGrids(): ParameterGrid[] {
  if (!adjustableOptions.value.length) return []

  const grids: ParameterGrid[] = []
  // 特殊处理的选项
  const checkOptions: ProgramAvailable.BooleanOption[] = []
  const customRecords: CustomConfigOptions = []

  adjustableOptions.value.forEach((option: ProgramAvailable.Option) => {
    const customParameter = getCustomParameter(option)
    if (customParameter) {
      // 暂存 CustomOptions 全部遍历完成后 再追加到 CheckOptions
      customRecords.push({ customOption: customParameter, option: option as unknown as ProgramAvailable.EnumOption })
    } else if (isPickerGrid(option) || isCustomPickerGrid(option)) {
      grids.push(getPickerGrid(option))
    } else if (isCheckboxGrid(option)) {
      // 暂存 CheckOptions 全部遍历完成后 再合并处理
      checkOptions.push(option as unknown as ProgramAvailable.BooleanOption)
    } else if (isRadioGrid(option)) {
      grids.push(getRadioGrid(option))
    }
    // 其他类型
    else {
      console.warn(`[Parameter View Warning]: ${option.key} has been ignored caused by mismatch any type.`)
    }
  })

  /**
   * 生成 Checkbox
   */
  const checkboxGrid = getCheckboxGrid(checkOptions, grids.length === 0)

  /**
   * 处理 Custom Options
   */
  const customOptions = getCustomOptions(customRecords)
  checkboxGrid.options = checkboxGrid.options.concat(customOptions)

  /**
   * 追加 Checkbox
   */
  if (checkOptions.length || customRecords.length) grids.push(checkboxGrid)

  return grids
}

function isCustomPickerGrid(option: ProgramAvailable.Option) {
  // 指定为 Picker 的值
  return props.pickerKeys?.includes(option.key)
}
function isPickerGrid(option: ProgramAvailable.Option) {
  // 数字类型  咖啡机COFFEE_BEAN_AMOUNT需要特殊处理
  return option.type === 'Int' || option.type === 'Double' || option.key === COFFEE_BEAN_AMOUNT
}
function getPickerGrid(option: ProgramAvailable.Option, isHalfPicker = false): ParameterGrid {
  // 取自 detail 的配置
  const detailOption = getDetailOption(option.key)

  // 选项数组
  let selectOptions: Option[] = []

  let optionUnit = (option as ProgramAvailable.NumberOption).unit || detailOption?.unit || ''
  const stepsize = (option as ProgramAvailable.NumberOption).constraints.stepsize
  if (optionUnit === 'seconds' && stepsize && stepsize % 60 === 0) {
    optionUnit = 'min'
  }
  if (option.type === 'Int' || option.type === 'Double') {
    // 数字类型处理
    selectOptions = getNumberOptions(option as ProgramAvailable.NumberOption)
  }
  // 枚举类型处理
  else if ((option as unknown as ProgramAvailable.EnumOption).constraints?.allowedvalues) {
    selectOptions = getEnumOptions(option as unknown as ProgramAvailable.EnumOption)
  }

  return {
    key: option.key,
    title: option.name,
    unit: generalUnitFormatter(option.key, detailOption?.value, optionUnit),
    type: 'Picker',
    options: selectOptions,
    value: detailOption?.value || null,
    isHalf: isHalfPicker,
  }
}

function isRadioGrid(option: ProgramAvailable.Option) {
  // 枚举类型
  return (
    option.type === 'Int' ||
    option.type === 'Double' ||
    (option as unknown as ProgramAvailable.EnumOption).constraints?.allowedvalues
  )
}
function getRadioGrid(option: ProgramAvailable.Option): ParameterGrid {
  // 取自 detail 的配置
  const detailOption = getDetailOption(option.key)

  // 选项数组
  let selectOptions: Option[] = getEnumOptions(option as unknown as ProgramAvailable.EnumOption)

  return {
    key: option.key,
    title: option.name,
    unit: generalUnitFormatter(option.key, detailOption?.value, option.unit),
    type: 'Radio',
    options: selectOptions,
    value: detailOption?.value || null,
  }
}

function isCheckboxGrid(option: ProgramAvailable.Option) {
  // 布尔类型   咖啡机COFFEE_BEAN_AMOUNT需要特殊处理
  return option.type === 'Boolean' && option.key !== COFFEE_BEAN_AMOUNT
}
function getCheckboxGrid(options: ProgramAvailable.Option[], noOtherGrids?: boolean): ParameterGrid {
  // 标题 (只有Checkbox类型时，显示为选项)
  const checkboxTitle = noOtherGrids ? '选项' : '其他（可多选）'
  const checkboxSelectOptions: Option[] = options.map((option) => {
    // 取自 detail 的配置
    const detailOption = getDetailOption(option.key)
    return {
      label: option.name || '',
      value: detailOption?.value || false,
      key: option.key,
    }
  })
  return {
    title: checkboxTitle,
    type: 'Checkbox',
    options: checkboxSelectOptions,
    key: 'Checkbox', // Checkbox 没有 Key
    value: null, // Checkbox作为一组Grid时 没有 Value
  }
}

function getCustomParameter(option: ProgramAvailable.Option) {
  // 自定义参数
  return props.customParams?.find((param) => param.key === option.key)
}
type CustomConfigOptions = { option: ProgramAvailable.EnumOption; customOption: CustomOption }[]
function getCustomOptions(options: CustomConfigOptions): Option[] {
  const customOptions = options.map<Option>(({ option, customOption }) => {
    // 取自 detail 的配置
    const detailOption = getDetailOption(option.key)
    return {
      label: option.name || '',
      value: detailOption?.value || '',
      key: option.key,
      customOption: {
        ...customOption,
        options: getEnumOptions(option).map(({ label, value }) => ({ label, value: value as string })),
      },
    }
  })
  return customOptions
}

/**
 * 工具方法
 */
function getDetailOption(parameterKey: string) {
  return detailOptions.value.find((option) => option.key === parameterKey)
}

defineExpose<{
  getParameterOptions: () => ProgramSelectOption[]
  getActivedParameterNames: () => string[]
}>({
  getParameterOptions,
  getActivedParameterNames,
})
</script>

<style lang="less">
.adjust-parameter-view {
  display: block;
  height: 100%;
  overflow: auto;
}
</style>
